{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using Simplicit's Easy API To Simulate Example Mesh\n",
    "[Simplicits](https://research.nvidia.com/labs/toronto-ai/simplicits/) is a mesh-free, representation-agnostic way to simulation elastic deformations. \n",
    "\n",
    "Here's a simple way to use the simplicit's code base. We can create a simple object, train it, simulate it and visualize all in a very few lines of code via our `easy_api`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.org/simple, https://pypi.ngc.nvidia.com\n",
      "Collecting gdown\n",
      "  Downloading gdown-5.2.0-py3-none-any.whl.metadata (5.8 kB)\n",
      "Collecting beautifulsoup4 (from gdown)\n",
      "  Downloading beautifulsoup4-4.13.3-py3-none-any.whl.metadata (3.8 kB)\n",
      "Requirement already satisfied: filelock in /home/vismaym/anaconda3/envs/sparsesimplicits/lib/python3.9/site-packages (from gdown) (3.17.0)\n",
      "Collecting requests[socks] (from gdown)\n",
      "  Downloading requests-2.32.3-py3-none-any.whl.metadata (4.6 kB)\n",
      "Requirement already satisfied: tqdm in /home/vismaym/anaconda3/envs/sparsesimplicits/lib/python3.9/site-packages (from gdown) (4.67.1)\n",
      "Collecting soupsieve>1.2 (from beautifulsoup4->gdown)\n",
      "  Downloading soupsieve-2.6-py3-none-any.whl.metadata (4.6 kB)\n",
      "Requirement already satisfied: typing-extensions>=4.0.0 in /home/vismaym/anaconda3/envs/sparsesimplicits/lib/python3.9/site-packages (from beautifulsoup4->gdown) (4.12.2)\n",
      "Collecting charset-normalizer<4,>=2 (from requests[socks]->gdown)\n",
      "  Downloading charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (35 kB)\n",
      "Collecting idna<4,>=2.5 (from requests[socks]->gdown)\n",
      "  Downloading idna-3.10-py3-none-any.whl.metadata (10 kB)\n",
      "Collecting urllib3<3,>=1.21.1 (from requests[socks]->gdown)\n",
      "  Downloading urllib3-2.3.0-py3-none-any.whl.metadata (6.5 kB)\n",
      "Collecting certifi>=2017.4.17 (from requests[socks]->gdown)\n",
      "  Downloading certifi-2025.1.31-py3-none-any.whl.metadata (2.5 kB)\n",
      "Collecting PySocks!=1.5.7,>=1.5.6 (from requests[socks]->gdown)\n",
      "  Downloading PySocks-1.7.1-py3-none-any.whl.metadata (13 kB)\n",
      "Downloading gdown-5.2.0-py3-none-any.whl (18 kB)\n",
      "Downloading beautifulsoup4-4.13.3-py3-none-any.whl (186 kB)\n",
      "Downloading certifi-2025.1.31-py3-none-any.whl (166 kB)\n",
      "Downloading charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (146 kB)\n",
      "Downloading idna-3.10-py3-none-any.whl (70 kB)\n",
      "Downloading PySocks-1.7.1-py3-none-any.whl (16 kB)\n",
      "Downloading soupsieve-2.6-py3-none-any.whl (36 kB)\n",
      "Downloading urllib3-2.3.0-py3-none-any.whl (128 kB)\n",
      "Downloading requests-2.32.3-py3-none-any.whl (64 kB)\n",
      "Installing collected packages: urllib3, soupsieve, PySocks, idna, charset-normalizer, certifi, requests, beautifulsoup4, gdown\n",
      "Successfully installed PySocks-1.7.1 beautifulsoup4-4.13.3 certifi-2025.1.31 charset-normalizer-3.4.1 gdown-5.2.0 idna-3.10 requests-2.32.3 soupsieve-2.6 urllib3-2.3.0\n"
     ]
    }
   ],
   "source": [
    "!pip install -q k3d\n",
    "!pip install gdown"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import copy, math, os, sys, logging, threading\n",
    "from typing import List, Tuple\n",
    "from pathlib import Path\n",
    "\n",
    "import numpy as np\n",
    "import torch\n",
    "import kaolin as kal\n",
    "\n",
    "from IPython.display import display\n",
    "from ipywidgets import Button, HBox, VBox\n",
    "\n",
    "#local logger, prints at info or above\n",
    "logging.basicConfig(level=logging.INFO, stream=sys.stdout)\n",
    "logger = logging.getLogger(__name__)\n",
    "\n",
    "#logger used in the api code\n",
    "logging.getLogger('kaolin.physics').setLevel(logging.INFO) # Prints everything at debug level or above\n",
    "\n",
    "\n",
    "def print_tensor(t, name='', **kwargs):\n",
    "    print(kal.utils.testing.tensor_info(t, name=name, **kwargs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get the data \n",
    "Get data from [this onedrive link](https://nvidia-my.sharepoint.com/:f:/p/vismaym/EnH63QdIGP9LlPefq-2ydTMBr9felItBPvfI30WunpQlzw?e=f3qiQS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set this to the path where you want to downloaded the mesh data\n",
    "mesh_data_dir = \"/home/vismaym/recode/TowakiSDFDataset/reconstructed_meshes/\" \n",
    "meshes = {}\n",
    "for mesh_file in os.listdir(mesh_data_dir):\n",
    "    mesh_path = os.path.join(mesh_data_dir, mesh_file)\n",
    "    meshes[mesh_file] = mesh_path"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loading Geometry\n",
    "Simplicits works with any geometry: meshes, pointclouds, SDFs, Gaussian splats, and more. For this example, we will use a mesh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SurfaceMesh object with batching strategy NONE\n",
      "            vertices: [21812, 3] (torch.float32)[cuda:0]  \n",
      "               faces: [43560, 3] (torch.int64)[cuda:0]  \n",
      "       face_vertices: if possible, computed on access from: (faces, vertices)\n",
      "        face_normals: if possible, computed on access from: (normals, face_normals_idx) or (vertex_normals, faces) or (vertices, faces)\n",
      "            face_uvs: if possible, computed on access from: (uvs, face_uvs_idx)\n",
      "      vertex_normals: if possible, computed on access from: (faces, face_normals)\n",
      "     vertex_tangents: if possible, computed on access from: (faces, face_vertices, face_uvs, vertex_normals)\n",
      "       vertex_colors: if possible, computed on access from: (faces, face_colors)\n",
      "     vertex_features: if possible, computed on access from: (faces, face_features)\n",
      "       face_tangents: if possible, computed on access from: (faces, vertex_tangents)\n",
      "         face_colors: if possible, computed on access from: (faces, vertex_colors)\n",
      "       face_features: if possible, computed on access from: (faces, vertex_features)\n"
     ]
    }
   ],
   "source": [
    "# Import and triangulate to enable rasterization; move to GPU\n",
    "mesh = kal.io.obj.import_mesh(meshes[\"SDF_Towaki_Capsule.obj\"], triangulate=True).cuda()\n",
    "mesh.vertices = kal.ops.pointcloud.center_points(mesh.vertices.unsqueeze(0), normalize=True).squeeze(0) \n",
    "orig_vertices = mesh.vertices.clone()  # Also save original undeformed vertices\n",
    "print(mesh)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sample Geometry\n",
    "To enable simulation we need point samples within the object's volume, and physical material parameters per point. Lets set this up."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9c8c1e3b424743d6b4b877fca2cc5b58",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Physics material parameters\n",
    "soft_youngs_modulus = 1e5\n",
    "poisson_ratio = 0.45\n",
    "rho = 500  # kg/m^3\n",
    "approx_volume = 0.5  # m^3\n",
    "\n",
    "# Points sampled over the object's bounding box\n",
    "num_samples = 100000\n",
    "uniform_pts = torch.rand(num_samples, 3, device='cuda') * (orig_vertices.max(dim=0).values - orig_vertices.min(dim=0).values) + orig_vertices.min(dim=0).values\n",
    "boolean_signs = kal.ops.mesh.check_sign(mesh.vertices.unsqueeze(0), mesh.faces, uniform_pts.unsqueeze(0), hash_resolution=512)\n",
    "\n",
    "# use pts within the object\n",
    "pts = uniform_pts[boolean_signs.squeeze()]\n",
    "yms = torch.full((pts.shape[0],), soft_youngs_modulus, device=\"cuda\")\n",
    "prs = torch.full((pts.shape[0],), poisson_ratio, device=\"cuda\")\n",
    "rhos = torch.full((pts.shape[0],), rho, device=\"cuda\")\n",
    "\n",
    "import k3d\n",
    "plot = k3d.plot()\n",
    "plot += k3d.points(pts.cpu().detach().numpy(), point_size=0.01)\n",
    "plot.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create and Train a SimplicitsObject\n",
    "We encapsulate everything Simplicits method needs to know about the simulated object in a `SimplicitsObject` instance. Once the object is created, we need to run training to learn reduced degrees of freedom our simulator can use. \n",
    "\n",
    "**This will take a couple of minutes.** Please be patient."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training simplicits object. This will take 2-3min. \n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 0, le: 442.7199401855469, lo: 19170456.0\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 1000, le: 603.0551147460938, lo: 41740.98046875\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 2000, le: 662.5147705078125, lo: 43167.28515625\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 3000, le: 577.317626953125, lo: 39980.375\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 4000, le: 1514.6077880859375, lo: 1725.7955322265625\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 5000, le: 893.8871459960938, lo: 2554.734130859375\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 6000, le: 857.1710205078125, lo: 2087.34765625\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 7000, le: 859.0032348632812, lo: 2339.7802734375\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 8000, le: 1204.2548828125, lo: 5165.48779296875\n",
      "INFO:kaolin.physics.simplicits.easy_api:Training step: 9000, le: 1544.3577880859375, lo: 2671.711669921875\n",
      "Object ready to simulate.\n"
     ]
    }
   ],
   "source": [
    "# Initialize and train a Simpicits object to enable simulation\n",
    "sim_obj = kal.physics.simplicits.SimplicitsObject(pts, yms, prs, rhos, torch.tensor([approx_volume], dtype=torch.float32, device=\"cuda\"), num_handles=5)\n",
    "print('Training simplicits object. This will take 2-3min. ')\n",
    "sim_obj.train(num_steps=10000)  # TODO: with next patch add log_interval=1000\n",
    "print('Object ready to simulate.')\n",
    "\n",
    "# sim_obj.load_model('/tmp/test_easy_api.pt') # if you saved previously trained object, you can load it instead"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optionally, you can save/load this pre-trained object\n",
    "# sim_obj.save_model('fox_mesh_model_10k_steps.pt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a Scene\n",
    "Now we are ready to set up all the forces in the scene to simulated as well as simulation settings. For example, here we will add gravity and a floor plane."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "scene = kal.physics.simplicits.SimplicitsScene() # default empty scene\n",
    "#Convergence might not be guaranteed with few newton iterations, but runs very fast\n",
    "scene.max_newton_steps = 3 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " The same `SimplicitsObject` can be added to multiple scene. Let's add it to our scene. Not we can reference it within the scene using `obj_idx`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "obj_idx = scene.add_object(sim_obj)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets set gravity and floor forces on the scene"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "scene.set_scene_gravity(acc_gravity=torch.tensor([0, 9.8, 0]))\n",
    "scene.set_scene_floor(floor_height=-0.8, floor_axis=1, floor_penalty=1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can play around with the material parameters of the object, indicated via `object_idx`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "scene.set_object_materials(obj_idx, yms=torch.tensor(1e4, device='cuda', dtype=torch.float))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set Up Rendering\n",
    "Let's set up rendering of our mesh so we can view it in a notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:root:Missing uvmap; cannot texturemap materials\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/vismaym/anaconda3/envs/sparsesimplicits/lib/python3.9/site-packages/torch/functional.py:539: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at /pytorch/aten/src/ATen/native/TensorShape.cpp:3637.)\n",
      "  return _VF.meshgrid(tensors, **kwargs)  # type: ignore[attr-defined]\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "328c99f6764b46ec90211d6ea8a6477a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Canvas(height=512, width=512)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ae9b44e6e33b4a90aaeb8f0d8813de44",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def render(in_cam):\n",
    "    active_pass = kal.render.easy_render.RenderPass.render\n",
    "    render_res = kal.render.easy_render.render_mesh(in_cam, mesh)\n",
    "\n",
    "    # use white background\n",
    "    img = render_res[active_pass]\n",
    "    background_mask = (\n",
    "        render_res[kal.render.easy_render.RenderPass.face_idx] < 0).bool()\n",
    "    img2 = torch.clamp(img, 0, 1)[0]\n",
    "    img2[background_mask[0]] = 1\n",
    "    final = (img2 * 255.).to(torch.uint8)\n",
    "    return {\"img\": final, \"face_idx\": render_res[kal.render.easy_render.RenderPass.face_idx].squeeze(0).unsqueeze(-1)}\n",
    "\n",
    "# faster low-res render during mouse motion\n",
    "def fast_render(in_cam, factor=8):\n",
    "    lowres_cam = copy.deepcopy(in_cam)\n",
    "    lowres_cam.width = in_cam.width // factor\n",
    "    lowres_cam.height = in_cam.height // factor\n",
    "    return render(lowres_cam)\n",
    "\n",
    "\n",
    "resolution = 512\n",
    "camera = kal.render.easy_render.default_camera(resolution).cuda()\n",
    "rest_state_viz = kal.visualize.IpyTurntableVisualizer(\n",
    "    resolution, resolution, copy.deepcopy(camera), render, fast_render=fast_render,\n",
    "    max_fps=24, world_up_axis=1)\n",
    "rest_state_viz.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## That's it! Let's Run and View or Physics Simulation\n",
    "All we need to do now is run simulation and display the object using Kaolin's in-notebook visualizer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:root:Missing uvmap; cannot texturemap materials\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f10da8d7e9d147939ed0bfda5af5165d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(Canvas(height=512, width=512), Button(description='Run Sim', style=ButtonStyle())))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ecfc27cea538465cb9b72e1f20c75fa6",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n",
      ".WARNING:root:Missing uvmap; cannot texturemap materials\n"
     ]
    }
   ],
   "source": [
    "# Reset mesh to its rest state\n",
    "mesh.vertices = orig_vertices\n",
    "\n",
    "fox_verts = []\n",
    "\n",
    "global sim_thread_open, sim_thread\n",
    "sim_thread_open = False\n",
    "sim_thread = None\n",
    "\n",
    "def run_sim():\n",
    "    scene.reset()  # reset internal simultion state\n",
    "\n",
    "    for s in range(50):\n",
    "        with visualizer.out:\n",
    "            scene.run_sim_step()\n",
    "            print(\".\", end=\"\")\n",
    "        mesh.vertices = scene.get_object_deformed_pts(obj_idx, orig_vertices).squeeze()\n",
    "        if s%10 == 0:\n",
    "            fox_verts.append(mesh.vertices.clone().detach())\n",
    "        visualizer.render_update()\n",
    "\n",
    "def start_simulation(b):\n",
    "    global sim_thread_open, sim_thread\n",
    "    with visualizer.out:\n",
    "        if(sim_thread_open):\n",
    "            sim_thread.join()\n",
    "            sim_thread_open = False\n",
    "        sim_thread_open = True\n",
    "        sim_thread = threading.Thread(target=run_sim, daemon=True)\n",
    "        sim_thread.start()\n",
    "\n",
    "scene.reset_object(obj_idx)\n",
    "button = Button(description='Run Sim')\n",
    "button.on_click(start_simulation)\n",
    "visualizer = kal.visualize.IpyTurntableVisualizer(\n",
    "    resolution, resolution, copy.deepcopy(camera), render, fast_render=fast_render,\n",
    "    max_fps=24, world_up_axis=1)\n",
    "visualizer.render_update()  # render first frame\n",
    "display(HBox([visualizer.canvas, button]), visualizer.out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "sparsesimplicits",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.21"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
